package com.ctshk.rpc.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ctshk.common.constant.SystemConstants;
import com.ctshk.common.enums.GlobalConstants;
import com.ctshk.common.enums.IsDeletedCode;
import com.ctshk.common.enums.SupplierStatus;
import com.ctshk.common.enums.SystemError;
import com.ctshk.common.exception.BusinessException;
import com.ctshk.common.utils.EntityUtil;
import com.ctshk.common.utils.SnowflakeIdWorker;
import com.ctshk.rpc.system.dto.SysMenuActionButtonDTO;
import com.ctshk.rpc.system.dto.SysMenuButtonDTO;
import com.ctshk.rpc.system.dto.SysMenuDTO;
import com.ctshk.rpc.system.entity.SysMenu;
import com.ctshk.rpc.system.entity.SysPermission;
import com.ctshk.rpc.system.entity.SysPermissionData;
import com.ctshk.rpc.system.entity.SysUser;
import com.ctshk.rpc.system.enums.MenuType;
import com.ctshk.rpc.system.enums.PermissionType;
import com.ctshk.rpc.system.mapper.*;
import com.ctshk.rpc.system.req.PermissionDataReq;
import com.ctshk.rpc.system.req.SysMenuReq;
import com.ctshk.rpc.system.req.SysPermissionReq;
import com.ctshk.rpc.system.service.ISysMenuService;
import com.ctshk.rpc.system.service.ISysPermissionDataService;
import com.ctshk.rpc.system.service.ISysPermissionService;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 菜单表 服务实现类
 * </p>
 *
 * @author 谢诗宏
 * @since 2020-12-23
 */
@DubboService
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements ISysMenuService {

    @Autowired
    private SysMenuMapper sysMenuMapper;
    @Autowired
    private SysPermissionMapper sysPermissionMapper;
    @Autowired
    private SysPermissionDataMapper sysPermissionDataMapper;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private SysRoleMapper sysRoleMapper;
    @Autowired
    private ISysPermissionService sysPermissionService;

    @Override
    @Transactional
    public int add(SysMenuReq sysMenuReq, long userId) {
        //父级父级ID
        Long partensId = null;
        //父级ID
        Long partenId = null;
        //顶层菜单ID
        Long topId = null;
        //不等于顶级菜单就看看他的父级和子级的关系是否正确
        if (0l != sysMenuReq.getParentId()) {
            SysMenu sysMenu = sysMenuMapper.selectById(sysMenuReq.getParentId());
            if (sysMenu == null) {
                throw new BusinessException(SystemError.SYSTEM_2111);
            }
            if (0l == sysMenu.getParentId()) {
                //父ID是顶层菜单ID就还可以加一层菜单
                if (sysMenuReq.getMenuType() - sysMenu.getMenuType() > 1) {
                    throw new BusinessException(SystemError.SYSTEM_2112);
                }
                topId = sysMenu.getId();//复制顶层菜单ID
            } else {
                //子级菜单类型减去父级菜单类型应该等于1 否则视为隔代关系
                if (sysMenuReq.getMenuType() - sysMenu.getMenuType() != 1) {
                    throw new BusinessException(SystemError.SYSTEM_2112);
                }
                partenId = sysMenu.getId();
                partensId = sysMenu.getParentId();//记录父级父级ID
            }
        }
        SysMenu sysMenu = new SysMenu();
        BeanUtils.copyProperties(sysMenuReq, sysMenu);
        sysMenu.setId(SnowflakeIdWorker.nextId());
        sysMenu.setCreateId(userId);
        sysMenu.setSysCode(SystemConstants.SYSTEM_CODE + RandomUtil.randomNumbers(8));
        int insert = sysMenuMapper.insert(sysMenu);
        if (insert > 0) {
            //尝试获取顶层ID
            if (topId == null && 0l != sysMenuReq.getParentId()) {
                SysMenu ppId = getPartentId(partensId);
                if (ppId == null) {
                    throw new BusinessException(SystemError.SYSTEM_2113);
                }
                topId = ppId.getId();
            }
            //不同的菜单默认不同的权限策略
            if (MenuType.MENU.getCode().equals(sysMenu.getMenuType()) || MenuType.PAGE.getCode().equals(sysMenu.getMenuType())) {
                SysPermissionReq spr = new SysPermissionReq();
                List<Long> ids = new ArrayList<>();
                ids.add(sysMenu.getId());
                spr.setIds(ids);
                spr.setUserId(userId);
                spr.setMenuType(sysMenu.getMenuType());
                spr.setTopId(topId == null ? 0l : topId);
                sysPermissionService.setMenuPermission(spr.getTopId(), spr.getIds(), spr.getUserId(), spr.getMenuType(), userId, false);
            } else if (MenuType.BUTTON.getCode().equals(sysMenu.getMenuType())) {
                PermissionDataReq pdr = new PermissionDataReq();
                pdr.setDataType(PermissionType.THIS.getCode());
                pdr.setMenuId(partenId);
                pdr.setUserId(userId);
                List<Long> buttonIds = new ArrayList<>();
                buttonIds.add(sysMenu.getId());
                pdr.setButtonIds(buttonIds);
                pdr.setAddFlage(false);
                sysPermissionService.setMenuPermissionData(pdr, userId);
            }
        }
        return insert;
    }

    /**
     * 递归尝试获取顶层ID
     *
     * @param partensId
     * @return
     */
    public SysMenu getPartentId(Long partensId) {
        SysMenu ppId = sysMenuMapper.selectById(partensId);
        if (ppId == null) {
            throw new BusinessException(SystemError.SYSTEM_2113);
        }
        if (ppId != null && 0l != ppId.getParentId()) {
            ppId = getPartentId(ppId.getParentId());
        }
        return ppId;
    }

    @Override
    public int del(long menuId, long userId) {
        SysMenu sysMenu = sysMenuMapper.selectById(menuId);
        if (sysMenu == null || sysMenu.getIsDeleted() == 1) {
            throw new BusinessException(SystemError.SYS_411);
        }
        if (SupplierStatus.ENABLE.getCode() == sysMenu.getStatus()) {
            throw new BusinessException(SystemError.SYS_443);
        }
        List<SysMenu> menus = sysMenuMapper.selectList(Wrappers.<SysMenu>lambdaQuery()
                .eq(SysMenu::getParentId, menuId)
                .eq(SysMenu::getStatus, SupplierStatus.ENABLE.getCode())
                .eq(SysMenu::getIsDeleted, IsDeletedCode.NO.getCode())
        );
        if (CollUtil.isNotEmpty(menus)) {
            throw new BusinessException(SystemError.SYSTEM_2010);
        }
        sysMenu.setIsDeleted(IsDeletedCode.YES.getCode());
        sysMenu.setModifiedId(userId);
        int i = sysMenuMapper.updateById(sysMenu);
        if (i > 0) {
            clearPermissions(sysMenu.getId());
        }
        return i;
    }

    /**
     * 菜单被删除或无效后要清理掉他们的权限
     *
     * @param menuId
     */
    private void clearPermissions(Long menuId) {
        if (menuId != null) {
            List<SysPermission> sysPermissions = sysPermissionMapper.selectList(Wrappers.<SysPermission>lambdaQuery()
                    .select(SysPermission::getId)
                    .eq(SysPermission::getMenuId, menuId)
            );
            if (sysPermissions != null && sysPermissions.size() > 0) {
                sysPermissionMapper.delete(Wrappers.<SysPermission>lambdaQuery()
                        .eq(SysPermission::getMenuId, menuId)
                );
                List<Long> collect = sysPermissions.stream().map(e -> e.getId()).collect(Collectors.toList());
                sysPermissionDataMapper.delete(Wrappers.<SysPermissionData>lambdaQuery()
                        .in(SysPermissionData::getPermissionId, collect)
                );
            }
        }
    }

    @Override
    @Transactional
    public int update(SysMenuReq sysMenuReq, long userId) {
        SysMenu sysMenu = sysMenuMapper.selectById(sysMenuReq.getId());
        if (sysMenu == null || sysMenu.getIsDeleted() == 1) {
            throw new BusinessException(SystemError.SYS_411);
        }
        sysMenu.setSort(sysMenuReq.getSort());
        sysMenu.setTitle(sysMenuReq.getTitle());
        sysMenu.setApiUrl(sysMenuReq.getApiUrl());
        //从有效设为无效时判断子级是否全部无效或被删除
//        if(SupplierStatus.DISABLE.getCode() != sysMenu.getStatus() &&
//                SupplierStatus.DISABLE.getCode() == sysMenuReq.getStatus()){
//            List<SysMenu> menus = sysMenuMapper.selectList(Wrappers.<SysMenu>lambdaQuery()
//                    .eq(SysMenu::getParentId, sysMenu.getId())
//                    .eq(SysMenu::getStatus, SupplierStatus.ENABLE.getCode())
//                    .eq(SysMenu::getIsDeleted, IsDeletedCode.NO.getCode())
//            );
//            if (CollUtil.isNotEmpty(menus)) {
//                throw new BusinessException(SystemError.SYSTEM_2010);
//            }
//            sysMenu.setStatus(sysMenuReq.getStatus());
//        }
        //状态发送改变时调用修改状态接口
        if (sysMenu.getStatus() != sysMenuReq.getStatus()) {
            setStatus(sysMenu.getId(), SupplierStatus.DISABLE.getCode(), userId);
        }
        sysMenu.setStatus(sysMenuReq.getStatus());
        return sysMenuMapper.updateById(sysMenu);
    }

    @Override
    public List<SysMenuDTO> query(Integer sysType, long menuId, long userId) {
        SysUser sysUser = sysUserMapper.selectById(userId);
        List<String> roles = sysRoleMapper.getRoleCode(sysUser.getRoles());
        boolean isAdmin = GlobalConstants.Role.isAdmins(roles);
        List<SysMenuDTO> result;
        // 未传菜单id时则只查询一级菜单并返回
        if (menuId == 0) {
            // 获取菜单前先验证角色,如是管理员则不严重权限,。。。这逻辑很蛋疼
            List<SysMenu> sysMenu;
            if (isAdmin) {
                sysMenu = sysMenuMapper.selectTop(sysType);
            } else {
                sysMenu = sysMenuMapper.selectTopByPermission(sysType, userId);
            }

            result = EntityUtil.copyList(sysMenu, SysMenuDTO.class);
            return result;
        }

        SysMenu sysMenu = sysMenuMapper.selectOne(Wrappers.<SysMenu>lambdaQuery()
                .eq(SysMenu::getId, menuId)
                .eq(SysMenu::getIsDeleted, 0));
        List<SysMenu> sysMenus;
        if (isAdmin) {
            sysMenus = sysMenuMapper.selectChildren(menuId);
        } else {
            sysMenus = sysMenuMapper.selectChildrenByPermission(menuId, userId);
        }

        if (sysMenu != null) {
            sysMenus.add(sysMenu);
        }

        TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
        // 自定义属性名
        // 默认支持排序
        treeNodeConfig.setWeightKey("sort");
        treeNodeConfig.setChildrenKey("children");

        //可配置树深度
        treeNodeConfig.setDeep(4);
        treeNodeConfig.setIdKey("id");
        List<Tree<String>> build = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(sysMenus)) {
            //转换器
            build = TreeUtil.build(sysMenus, "0", treeNodeConfig,
                    (treeNode, tree) -> {
                        tree.setId(treeNode.getId().toString());
                        tree.setParentId(treeNode.getParentId().toString());
                        // tree.setWeight(treeNode.getOrderNum());
                        tree.setName(treeNode.getTitle());

                        // 扩展属性
                        tree.putExtra("key", treeNode.getId());
                        tree.putExtra("title", treeNode.getTitle());
                        tree.putExtra("sysCode", treeNode.getSysCode());
                        tree.putExtra("apiUrl", treeNode.getApiUrl());
                        tree.putExtra("businessCode", treeNode.getBusinessCode());
                        tree.putExtra("menuType", treeNode.getMenuType());
                        tree.putExtra("sort", treeNode.getSort());
                        tree.putExtra("status", treeNode.getStatus());
                    });
        }

        result = JSONArray.parseArray(JSONObject.toJSONString(build), SysMenuDTO.class);
        return result;
    }

    @Override
    public List<SysMenuDTO> queryAll(Integer sysType, long userId) {
        List<SysMenu> sysMenus = sysMenuMapper.selectALLByPermission(sysType, userId);

        TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
        // 自定义属性名
        // 默认支持排序
        treeNodeConfig.setWeightKey("sort");
        treeNodeConfig.setChildrenKey("children");

        //可配置树深度
        treeNodeConfig.setDeep(4);
        treeNodeConfig.setIdKey("id");

        //转换器
        List<Tree<String>> build = TreeUtil.build(sysMenus, "0", treeNodeConfig,
                (treeNode, tree) -> {
                    tree.setId(treeNode.getId().toString());
                    tree.setParentId(treeNode.getParentId().toString());
                    tree.setName(treeNode.getTitle());

                    tree.putExtra("key", treeNode.getId());
                    tree.putExtra("title", treeNode.getTitle());
                    tree.putExtra("sysCode", treeNode.getSysCode());
                    tree.putExtra("apiUrl", treeNode.getApiUrl());
                    tree.putExtra("businessCode", treeNode.getBusinessCode());
                    tree.putExtra("menuType", treeNode.getMenuType());
                    tree.putExtra("sort", treeNode.getSort());
                    tree.putExtra("permissionId", treeNode.getPermissionId());
                });

        List<SysMenuDTO> result = JSONArray.parseArray(JSONObject.toJSONString(build), SysMenuDTO.class);
        return result;
    }

    @Override
    public List<SysMenuActionButtonDTO> queryActionButton(long menuId, long userId) {
        List<SysMenu> sysMenus = sysMenuMapper.selectList(Wrappers.<SysMenu>lambdaQuery()
                .eq(SysMenu::getParentId, menuId)
                .eq(SysMenu::getMenuType, 3)
                .eq(SysMenu::getStatus, 1)
                .eq(SysMenu::getIsDeleted, 0));

        List<Object> sysMenuIds = sysPermissionMapper.selectObjs(Wrappers.<SysPermission>lambdaQuery()
                .select(SysPermission::getMenuId)
                .eq(userId != 0, SysPermission::getUserId, userId)
                .eq(SysPermission::getMenuType, 3));

        List<SysMenuActionButtonDTO> sysMenuActionButtonDTOS = EntityUtil.copyList(sysMenus, SysMenuActionButtonDTO.class);
        sysMenuActionButtonDTOS.forEach(sysMenuActionButtonDTO -> {
            sysMenuActionButtonDTO.setChecked(sysMenuIds.contains(sysMenuActionButtonDTO.getId()));
        });

        return sysMenuActionButtonDTOS;
    }

    @Override
    public List<Long> queryChildrenIds(long menuId, long userId) {
        List<SysMenu> sysMenus = sysMenuMapper.selectChildrenStatusByPermission(menuId, userId);
        List<Long> buttonIds = new ArrayList<>();
        sysMenus.forEach(sysMenu -> {
            buttonIds.add(sysMenu.getId());
        });

        return buttonIds;
    }

    @Override
    public List<Long> queryActionButtonIds(long menuId, long userId, boolean isAdmin) {
        List<SysMenu> sysMenus = new ArrayList<>();
        if (isAdmin) {
            sysMenus = sysMenuMapper.selectChildrenButtonStatus(menuId);
        } else {
            sysMenus = sysMenuMapper.selectChildrenButtonStatusByPermission(menuId, userId);
        }
        List<Long> buttonIds = new ArrayList<>();
        sysMenus.forEach(sysMenu -> {
            buttonIds.add(sysMenu.getId());
        });
        return buttonIds;
    }

    @Override
    public List<SysMenuButtonDTO> queryActionButtonAllIds(long userId, boolean isAdmin, String parentIds) {
        List<SysMenuButtonDTO> sysMenus = new ArrayList<>();
        if (isAdmin) {
            sysMenus = sysMenuMapper.selectChildrenButtonStatusAll();
        } else {
            sysMenus = sysMenuMapper.selectChildrenButtonStatusListByPermission(userId);
        }
        return sysMenus;
    }

    @Override
    public void setStatus(long menuId, int status, long userId) {
        List<SysMenu> sysMenus = sysMenuMapper.selectChildren(menuId);
        if (status == SupplierStatus.ENABLE.getCode()) {
            sysMenus.addAll(sysMenuMapper.selectParents(menuId));
        } else if (status == SupplierStatus.DISABLE.getCode()) {
            sysMenus.add(sysMenuMapper.selectById(menuId));
        }
        sysMenus.forEach(sysMenu -> {
            sysMenu.setStatus(status);
            sysMenu.setGmtModified(LocalDateTime.now());
            sysMenu.setModifiedId(userId);
        });
        super.updateBatchById(sysMenus);
    }

    @Override
    public List<Object> queryAllByUserId(Integer sysType, long userId, boolean isAdmin) {
        List<Object> menuIds = sysPermissionMapper.selectObjs(Wrappers.<SysPermission>lambdaQuery()
                .select(SysPermission::getMenuId)
                .eq(!isAdmin, SysPermission::getUserId, userId)
                .ne(SysPermission::getMenuType, 3));
        return menuIds;
    }

}
